`timescale 1 ns /  100 ps
module uart_tx
(
	clock,
	resetn,
	data,
	start,
	q,
	done
);

input clock, resetn, start;
input [7:0] data;
output q, done;

reg [2:0] state;
reg [2:0] next_state;
parameter IDLE = 3'b000, TRANSMIT = 3'b001, DONE = 3'b010, START = 3'b011, STOP = 3'b100;

// tick generator
wire clock_tick;
wire tick_clear;
assign tick_clear = (state == IDLE);
tick_generator tick_generator_instance (.clock(clock), .resetn(resetn), .clear(tick_clear), .tick(clock_tick));
defparam tick_generator_instance.step_size = 20'd2416;

// transmit 8 bit at a time
reg [2:0] count;

// control FSM
always @(*)
begin
	if(resetn == 0)
		next_state = IDLE;
	else
		case (state)
			IDLE:
				if (start)
					next_state = START;
				else
					next_state = IDLE;
			START:
				if (clock_tick)
					next_state = TRANSMIT;
				else
					next_state = START;
			TRANSMIT:
				if (count == 3'd7 && clock_tick)
					next_state = STOP;
				else
					next_state = TRANSMIT;
			STOP:
				if (clock_tick)
					next_state = DONE;
				else
					next_state = STOP;
			DONE:
				if(start == 1'b0)
					next_state = IDLE;
				else
					next_state = DONE;
		endcase
end

always @(posedge clock or negedge resetn)
begin
	if(resetn == 0)
		state <= IDLE;
	else
		state <= next_state;
end

// counter
wire increment;
assign increment = (state == TRANSMIT && clock_tick);
always @(posedge clock or negedge resetn)
begin
	if (resetn == 0)
		count <= 0;
	else if (increment)
		count <= count + 3'b1;
end

// shift register
reg [7:0] transmit;
wire load_data;
assign load_data = (state == IDLE && start == 1'b1) ? 1'b1 : 1'b0;
always @(posedge clock or negedge resetn)
begin
	if (resetn == 0)
		transmit <= 0;
	else if (load_data)
		transmit <= data;
	else if (increment)
		transmit <= transmit >> 1;
end

// tx data
assign q = (state == TRANSMIT) ? transmit[0] : (state == START) ? 1'b0 : 1'b1;

// done signal
assign done = (state == DONE);

endmodule

// tick generator
module tick_generator
(
	clock,
	resetn,
	clear,
	tick
);
	input clock, resetn, clear;
	output reg tick;
	
	reg [19:0]accumulator;
	// step size => baudrate
	// 403 => 19200
	// 2416 => 115200
	// 2684 => 128000(127983)
	// 5369 => 256000
	parameter step_size = 20'd2416;
	always @(posedge clock or negedge resetn)
	begin
		if (resetn == 0)
		begin
			accumulator <= 0;
			tick <= 0;
		end
		else if (clear == 1)
		begin
			accumulator <= 0;
			tick <= 0;
		end
		else
			{tick, accumulator[19:0]} <= accumulator + step_size;
	end
endmodule

